// .hpp是一种开源C++头文件和源文件的写法
// header only 开源代码 --- 把类的声明和类的方法实现放到一块儿，因为开源代码是公开的，那么这样在使用开源代码的时候，
// 就可以直接include <xxx.hpp>，就可以直接使用整个的类和内部方法了，不用像写一些工程项目时，.cpp和.h分开那样了。
//.hpp这样的文件使用的时候，就可以像使用小组件一样直接去使用，使用起来会很方便！

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cassert>
#include <functional>
#include <pthread.h>

class Thread;//声明类

//线程上下文，当成大号结构体来使用
class Context
{
public:
    Thread *_this;
    void *_args;
public:
    Context():_this(nullptr), _args(nullptr)
    {}
    ~Context()
    {}
};

class Thread
{
public:
    // using func_t = std::function<void* (void*)>;// using是C++11的写法
    typedef std::function<void* (void*)> func_t;
    const int num = 1024;
public:
    Thread(func_t func, void *args, int number):_func(func), _args(args)
    {
        // C++写法:
        // std::string name = "thread-";
        // name += std::to_string(number);
        char buffer[num];
        snprintf(buffer, sizeof buffer, "thread-%d", number);
        _name = buffer;

        Context *ctx = new Context;
        ctx->_this = this;
        ctx->_args = _args;
        
        
        // int n = pthread_create(&_tid, nullptr, start_routine, (void *)&ctx); //TODO
        int n = pthread_create(&_tid, nullptr, start_routine, ctx); //TODO
        assert(n == 0);//编译以dubug版本发布时，assert存在。release版本发布时，assert不存在，n定义了但未被使用，有些编译器下会有warning
        (void)n;//防止release版本下某些编译器报warning
    }
    //在类内创建线程，想让线程执行类内方法，需要将方法设置称static，这样就没有this指针的干扰了
    static void *start_routine(void *args)
    //类内成员，第一个位置有缺省参数this指针，所以和pthread_create的第三个参数类型不匹配。但只要我们改成static，就可以和void*(xxx*)(void*)函数指针匹配了
    {
        // 因为没有this指针，所以静态成员不能调用非静态成员方法或变量，只能去调用静态成员方法或变量！
        //但我们不推荐将_func成员变量搞成static修饰，那就属于整个类了，所有的线程对象的_func都是一样的了，我们并不希望看到这样的结果。

        // return _func(args);
        //解决方案1: 把函数搞到外面，通过友元来解决，但这样也不太好，类封装被破坏了。
        //解决方案2: 虽然静态成员没有this指针，但我们可以搞一个Context的结构体，让这个结构体中某个字段存储this指针，通过结构体中的this指针来调用_func函数。
        //          静态函数通过 含有this指针的中间件ctx 来调用非静态成员函数run()方法，run方法内部再执行_func()
        Context *ctx = static_cast<Context *>(args);
        void *ret = ctx->_this->run(ctx->_args);
        delete ctx;

        return ret;
    }
    void *run(void *args)
    {
        return _func(args);
    }
    //不要start，直接像C++那样，构造函数之后就可以直接让线程跑起来
    // void start()
    // {
    //     // 异常 == if : 意料之外用异常或者if判断 
    //     // assert : 意料之中用assert判断

    //     // Context ctx;
    //     // // ctx._this(this);//这么写g++可能认为你再进行函数调用
    //     // // ctx._args(_args);
    //     // ctx._this = this;
    //     // ctx._args = _args;

    //     //也可以通过new的方式去写，给线程函数传参的时候，直接传就行，不用取地址了就
    //     Context *ctx = new Context;
    //     ctx->_this = this;
    //     ctx->_args = _args;
        
        
    //     // int n = pthread_create(&_tid, nullptr, start_routine, (void *)&ctx); //TODO
    //     int n = pthread_create(&_tid, nullptr, start_routine, ctx); //TODO
    //     assert(n == 0);//编译以dubug版本发布时，assert存在。release版本发布时，assert不存在，n定义了但未被使用，有些编译器下会有warning
    //     (void)n;//防止release版本下某些编译器报warning
    // }

    void join()
    {
        int n = pthread_join(_tid, nullptr);
        assert(n == 0);
        (void)n;      
    }

    ~Thread()
    {
        // do nothing
    }

private:
    std::string _name;
    func_t _func;
    void *_args;//执行线程函数时，对应的参数

    pthread_t _tid;
};
















